#include "..\inc\swilib.h"
#include "conf_loader.h"
#include "ilock.h"

typedef struct
{
  CSM_RAM csm;
  int gui_id;
}MainCSM;

typedef struct
{
  GUI gui;
}MainGUI;

extern const char realpass[32];
//extern const char title_str[128];
//extern const RECT title_pos;
//extern const char title_bg_color[4];
//extern const char title_color[4];
//extern const unsigned int title_font;
extern const char message_str[128];
extern const char message_bg_color[4];
extern const char message_color[4];
extern const unsigned int message_font;
extern const char password_color[4];
extern const unsigned int password_font;
extern const char sk_color[4];
extern const unsigned int hot_key;
extern const unsigned int lock_keypad;
extern const unsigned int xtaskable;
extern const unsigned int start_lock;
extern const char exit_code[32];

unsigned int maincsm_id=0;
unsigned int guicsm_id=0;
unsigned int maingui_id=0;
char password[32]="\0";
int int_pwd=0;
int is_gui_started=0;
int is_valid_pass=-1;

int is_gui_focused=0;
int is_hook_end=0;
int to_exit=0;

int hot_key_state=2;  // down 0, pressed 1, released 2

int scrw;
int scrh;

extern void kill_data(void *p, void (*func_p)(void *));

#pragma inline
int get_string_width(WSHDR *ws, int font)
{
  int width=0;
  unsigned short *body=ws->wsbody;
  int len=body[0];
  while(len)
  {
    width+=GetSymbolWidth(body[len],font);
    len--;
  }
  return (width);
}

#pragma inline
void patch_rect(RECT*rc,int x,int y, int x2, int y2)
{
  rc->x=x;
  rc->y=y;
  rc->x2=x2;
  rc->y2=y2;
}

#pragma inline=forced
int toupper(int c)
{
  if ((c>='a')&&(c<='z')) c+='A'-'a';
  return(c);
}

#pragma inline
int strcmp_nocase(const char *s1,const char *s2)
{
  int i;
  int c;
  while(!(i=(c=toupper(*s1++))-toupper(*s2++))) if (!c) break;
  return(i);
}

void print_soft_key(void)
{
  char str_bksp[]="Bksp";
  char str_clear[]="Clear";
  char str_OK[]="OK";
  WSHDR *wsl = AllocWS(16);
  WSHDR *wsm = AllocWS(16);
  WSHDR *wsr = AllocWS(16);
  
  utf8_2ws(wsl,str_OK,strlen(str_OK));
  utf8_2ws(wsm,str_clear,strlen(str_clear));
  utf8_2ws(wsr,str_bksp,strlen(str_bksp));
  // print right key
  DrawString(wsr,
             scrw-get_string_width(wsr,FONT_MEDIUM)-4,
             scrh-GetFontYSIZE(FONT_MEDIUM)-2,
             scrw,
             scrh,
             FONT_MEDIUM,32,sk_color,GetPaletteAdrByColorIndex(23)); 
  // print middle key
  DrawString(wsm,
             (int)(ScreenW()/2)-GetFontYSIZE(FONT_MEDIUM)-2,
             scrh-GetFontYSIZE(FONT_MEDIUM)-2,
             scrw,
             scrh,
             FONT_MEDIUM,32,sk_color,GetPaletteAdrByColorIndex(23)); 
  // print left key
  DrawString(wsl,
             2,
             scrh-GetFontYSIZE(FONT_MEDIUM)-2,
             scrw,
             scrh,
             FONT_MEDIUM,32,sk_color,GetPaletteAdrByColorIndex(23)); 
  
  FreeWS(wsr);
  FreeWS(wsm);
  FreeWS(wsl);
}

void onRedraw(MainGUI *data)
{
#ifdef ELKA
  DisableIconBar(1);
#endif
  // draw background
  DrawRectangle(0,0,scrw,scrh,0,message_bg_color,message_bg_color);
  WSHDR *ws = AllocWS(128);
  char show_pass[32]="\0";
  int i;
  // print soft keys
  print_soft_key();
  // mark password
  for(i=0;i<int_pwd;i++)
  {
    show_pass[i]='*';
    show_pass[i+1]='\0';
  }
  // show warning message
  utf8_2ws(ws,message_str,strlen(message_str));
  DrawString(ws,
             0,
             0,
             scrw,
             scrh,
             message_font,TEXT_ALIGNMIDDLE+TEXT_OUTLINE,message_color,GetPaletteAdrByColorIndex(23)); 
  // show input passward promote
  wsprintf(ws, "Password:\n%s",show_pass);
  DrawString(ws,
             0,
             scrh/2,
             scrw,
             scrh,
             password_font,TEXT_ALIGNMIDDLE+TEXT_OUTLINE,password_color,GetPaletteAdrByColorIndex(23)); 
  FreeWS(ws);
}

int OnKey(MainGUI *data, GUI_MSG *msg)
{
  int i_key=msg->gbsmsg->submess;
  SetIllumination(0, 1, 10, 0);
  DirectRedrawGUI();
  
  // keydown
  if (msg->gbsmsg->msg==KEY_DOWN)
  {
    // accept number
    if ((i_key >= '0')&&(i_key <= '9')&&int_pwd<=31)
    {
      password[int_pwd]=i_key;
      int_pwd++;
      if(int_pwd!=31)
        password[int_pwd]=0;
      return(0);
    }
    
    // OK
    if(i_key==LEFT_SOFT)
    {
      // exit code matched
      if( (strlen(password)==strlen(exit_code))
          &&!(strncmp(password,exit_code,strlen(password))) )
      {
        CloseGUI(guicsm_id);
        to_exit = 1;
        return(0);
      }
        
      // password  matched
      if( (strlen(password)==strlen(realpass))
         &&!(strncmp(password,realpass,strlen(password))) )
      {        
        CloseGUI(guicsm_id);
        return(0);
      }      
      else  // dismatched
      {
        return(0);
      }
    }
      
    // Clear
    if (i_key == UP_BUTTON)
    {
      int_pwd = 0;
      password[0] = '\0';
      return(0);
    }
    
    // Backspace
    if(i_key==RIGHT_SOFT)
    {
      if(int_pwd)
      {
        int_pwd--;
        password[int_pwd]=0;
      }
      return(0);
    }
    
  } // if keydown
  return(0);
}


// gui.state :
//  0 - closed
//  1 - started (unfocused)
//  2 - focused
void onCreate(MainGUI *data, void *(*malloc_adr)(int))
{
  data->gui.state=1;
}

void onClose(MainGUI *data, void (*mfree_adr)(void *))
{
  data->gui.state=0;
  is_gui_focused = 0;
}

void onFocus(MainGUI *data, void *(*malloc_adr)(int), void (*mfree_adr)(void *))
{
  DisableIDLETMR();
  data->gui.state=2;
  is_gui_focused = 1;
}

void onUnfocus(MainGUI *data, void (*mfree_adr)(void *))
{
  is_gui_focused = 0;
  if (data->gui.state!=2)
    return;
    else data->gui.state=1;
}

int method8(void){return(0);}
int method9(void){return(0);}

const void * const gui_methods[11]={
	(void *)onRedraw,	//Redraw
	(void *)onCreate,	//Create
	(void *)onClose,	//Close
	(void *)onFocus,	//Focus
	(void *)onUnfocus,	//Unfocus
	(void *)OnKey,		//OnKey
	0,
	(void *)kill_data,	//Destroy
	(void *)method8,
	(void *)method9,
	0
};

void GUI_onclose(CSM_RAM *csm)
{
}

int GUI_onmessage(CSM_RAM *data, GBS_MSG *msg)
{
  MainCSM *csm=(MainCSM *)data;
  // destroy
  if ((msg->msg==MSG_GUI_DESTROYED)&&((int)msg->data0==csm->gui_id))
  {
    csm->csm.state=-3;
  }
  return(1);
}

const RECT Canvas={0,0,0,0};
void GUI_oncreate(CSM_RAM *data)
{
  MainCSM *csm=(MainCSM *)data;
  MainGUI *main_gui=malloc(sizeof(MainGUI));
  zeromem(main_gui,sizeof(MainGUI));	
  patch_rect((RECT*)&Canvas,0,0,scrw,scrh);
  main_gui->gui.canvas=(void *)(&Canvas);
  main_gui->gui.flag30=2;
  main_gui->gui.methods=(void *)gui_methods;
  main_gui->gui.item_ll.data_mfree=(void (*)(void *))mfree_adr();	
  csm->csm.state=0;
  csm->csm.unk1=0;
  csm->gui_id=CreateGUI(main_gui);
  maingui_id=csm->gui_id;
}

const int minus11=-11;

// the gui to be shown with csm
const CSM_DESC CSM_GUI=
{
  GUI_onmessage,
  GUI_oncreate,
#ifdef NEWSGOLD
  0,
  0,
  0,
  0,
#endif
  GUI_onclose,
  sizeof(MainCSM),
  1,
  &minus11
};

int StartGUI(void)
{
  int _guicsm_id = 0;
  LockSched();
  char dummy[sizeof(MainCSM)];
  _guicsm_id = CreateCSM(&CSM_GUI,dummy,2);  // 2 - show GUI
  is_gui_started = 1;
  is_gui_focused = 1;
  is_valid_pass = 0;
  // lock keypad
  if(lock_keypad)
  {
    KbdLock();  
    is_valid_pass=0; 
  }
  UnlockSched();
  return _guicsm_id;
}

void CloseGUI(int _guicsm_id)
{
  LockSched();
  is_gui_started = 0;
  is_gui_focused = 0;
  is_valid_pass = 1;  
  int_pwd=0;
  password[0]='\0';
  
  if(lock_keypad)
  {
    KbdUnlock();
    is_valid_pass=-1; // -1 to reset
  }
  
  CloseCSM(_guicsm_id);
  
  UnlockSched();
}

// hook hotkey to activate program to show GUI on top
// hook RED_BUTTON to disable closing program by force
int my_keyhook(int submsg, int msg)
{
  // hook for hotkey action when not showing gui
  // hotkey mode is long press
  if(!(is_gui_started) && (submsg==hot_key))
  {
    switch(msg)
    {
    case KEY_DOWN:
      hot_key_state=0; // mark key down
      return KEYHOOK_BREAK; // do nothing
      
    case LONG_PRESS:
      hot_key_state=1; // mark keypressed
      guicsm_id = StartGUI();  // show GUI
      return KEYHOOK_BREAK;
      
    case KEY_UP:
      if(hot_key_state==0) // up from down 
      {
        hot_key_state=2;
        GBS_SendMessage(MMI_CEPID,KEY_DOWN,hot_key);
        return KEYHOOK_NEXT;
      }else // up from pressed
      {
        hot_key_state=2;
        return KEYHOOK_BREAK;
      }
    } // swith key action
    
  } // if not showing
  
  // disable RED_BUTTON
  if (is_gui_started && is_gui_focused && (submsg == RED_BUTTON))
  {
    return KEYHOOK_BREAK;
  }
  
  // enable/disable killed by XTask
  // disable lock keypad by '#'
  if (is_gui_started && is_gui_focused && (submsg == '#'))
  {
    if(xtaskable && (submsg!=LONG_PRESS))
      return KEYHOOK_NEXT;
    else
      return KEYHOOK_BREAK;
  }
  
  // others
  return KEYHOOK_NEXT;
}

int CSM_onmessage(CSM_RAM* data,GBS_MSG* msg)
{  
  // update bcfg
  if (msg->msg==MSG_RECONFIGURE_REQ)
  {
    extern const char *successed_config_filename;
    if (strcmp_nocase(successed_config_filename,(char *)msg->data0)==0)
    {
      ShowMSG(1,(int)"iLock config updated.");
      InitConfig();
      
      if (xtaskable == is_hook_end)
      {
        RemoveKeybMsgHook((void*)my_keyhook);
        is_hook_end = AddKeyHook(!xtaskable);
      }
    }
  }
  
  if(to_exit)
  {
    MainCSM *csm=(MainCSM *)data;
    csm->csm.state=-3;
  }
    
  return(1);
}

static void CSM_oncreate(CSM_RAM *data)
{
}

static void CSM_onclose(CSM_RAM *csm)
{
  ELF_Destroy();
}

static unsigned short maincsm_name_body[140];
static const struct
{
  CSM_DESC maincsm;
  WSHDR maincsm_name;
}CSM_iLock =
{
  {
  CSM_onmessage,
  CSM_oncreate,
#ifdef NEWSGOLD
  0,
  0,
  0,
  0,
#endif
  CSM_onclose,
  sizeof(MainCSM),
  1,
  &minus11
  },
  {
    maincsm_name_body,
    NAMECSM_MAGIC1,
    NAMECSM_MAGIC2,
    0x0,
    139
  }
};

static void UpdateCSMname(void)
{
  wsprintf((WSHDR *)(&CSM_iLock.maincsm_name),"iLock");
}

// Return:
//  1 - hook end
//  0 - general hooked
int AddKeyHook(int hook_end)
{
  if(!hook_end)
  {
    AddKeybMsgHook((void *)my_keyhook);
    ShowMSG(1,(int)"General Hooked.");
    return 0;
  }
  else
  {
    if(AddKeybMsgHook_end((void *)my_keyhook))
    {
      ShowMSG(1,(int)"Success Hooked.");
      return 1;
    }
    else
    {
      MsgBoxError(1,(int)"Failed in Ended-Hook.");
      AddKeybMsgHook((void *)my_keyhook);
      ShowMSG(1,(int)"General Hooked.");
      return 0;
    }
  }
}

void ELF_Destroy(void)
{
  extern void *ELF_BEGIN;
  RemoveKeybMsgHook((void *)my_keyhook);
  kill_data(&ELF_BEGIN,(void (*)(void *))mfree_adr());
}

int main(void)
{
  CSM_RAM *save_cmpc;
  char dummy[sizeof(MainCSM)];
  InitConfig();  

  is_hook_end = AddKeyHook(!xtaskable);
  
  UpdateCSMname();
  scrw=ScreenW()-1;
  scrh=ScreenH()-1;
  
  LockSched();
  
  save_cmpc=CSM_root()->csm_q->current_msg_processing_csm;
  CSM_root()->csm_q->current_msg_processing_csm=CSM_root()->csm_q->csm.first;
  // CSM starting...
  maincsm_id = CreateCSM(&CSM_iLock.maincsm,dummy,0);  // 0 indicates run in background, 2 show gui
  
  CSM_root()->csm_q->current_msg_processing_csm=save_cmpc;
  
  // whether to lock immdiatally after start
  if (start_lock)
    StartGUI();
  
  UnlockSched();
    
  return 0;
}
